Spilled Secrets: Uncovering the Effects of Oil Spills on the North Sea Ecosystem¶

By: Ryan Boarman¶

Why oil spills are a concern:¶

Oil spills and their effects on the marine ecosystem is important and pose a serious threat to the environment, causing damage to marine habitats, wildlife, and biodiversity. Oil spills affect the interconnected ecosystems and the long-term health of the ocean.

Furthermore, oil spills have far-reaching consequences for society. They negatively impact the fishing, tourism, and public health sectors, leading to social conflicts, policy problems, and substantial financial burdens related to cleanup efforts and damage restoration. The issue also contributes to climate change, highlighting the need for sustainable management and stricter regulations.

Understanding the consequences of oil spills in the North Sea underlines the importance of striking a balance between economic growth, environmental conservation, and societal well-being. This project will provide valuable insights to guide policymakers and industry practices towards more sustainable management, monitoring, and conservation efforts, ultimately benefiting both human and marine life in the long run Oil Spill

Image Credit: Copernicus.eu

Prior research:¶

Numerous studies and initiatives have been conducted in the area of oil spills and their impact on the marine ecosystem. For instance, the Deepwater Horizon oil spill in 2010, one of the largest marine oil spills in history, led to extensive research on environmental impacts, effects on marine life, and response techniques. Reserach conducted during the Deepwater Horizon spill found severe lung diseases in dolphins due to the oil (Schwacke et al., 2014). The earlier Exxon Valdez oil spill in 1989 also prompted a surge in research on oil spill response, cleanup technologies, and environmental impact assessments.

International conventions and organizations, such as the International Maritime Organization (IMO) and the International Convention for the Prevention of Pollution from Ships (MARPOL), have established guidelines and regulations for preventing and responding to oil spills. The European Maritime Safety Agency (EMSA) and the Bonn Agreement have specifically focused on the North Sea region, working to prevent pollution from ships and offshore installations, and promoting cooperation between countries in case of incidents. Researchers have utilized satellite imagery, like Sentinel-1, to detect and monitor oil spills in the ocean. Organizations like SkyTruth use remote sensing techniques to track oil slicks and leaks from offshore platforms, providing valuable data for researchers and authorities. Ecological impact studies have examined the effects of oil spills on marine life, focusing on behavior, reproduction, survival, and population dynamics. These studies help in understanding the long-term consequences of oil spills on the marine ecosystem and inform future conservation efforts.

Despite the considerable work already done in this area, ongoing research is crucial for understanding the evolving nature of oil spills, improving response strategies, and addressing the long-term impacts on marine ecosystems.

Oil Spill

Image Credit: NBC News

Schwackeet al. (2014) Health of common bottlenose dolphins (Tursiops truncatus) in Barataria Bay, Louisiana, following the Deepwater Horizon oil spill. Environmental Science and Technology. 48(1): 93-103. doi: 10.1021/es403610f.

Methodlogy¶

Two main data sources were used for this exploratory analysis. Oil slick locations and species distribution maps.

Oil slicks were provided by SkyTruth. SkyTruth’s project Cerulean is an algorithm designed to detect oil slicks using Synthetic Aperture Radar (SAR) imagery. The algorithm scans Sentinel-1 SAR imagery with a focus on vertical polarization emitted and received (VV) for dark, smooth areas that indicate oil slicks. SAR imagery is useful for this purpose because of its ability to penetrate clouds and its 6-day temporal resolution, allowing for timely and frequent acquisition of images. Additionally, the contrast between the surface scattering of radar pulses off the small wavelets in clean water versus the smoother, darker appearance of oil slicks on radar images makes SAR imagery a useful tool for detecting oil spills (SkyTruth, 2023). I received oil slicks from 2020 in the North Sea, for this proof of concept and work flow I used only oil slicks from August 2020. I filtered the oil slick by category to only included "Infrastructure", "Vessel Old", "Recent", and "Adjacent.

Species distribution maps used in this analysis are from Waggitt et al. 2019. Waggit et al. generated monthly distribution maps of cetaceans and seabirds. A comprehensive survey of the North-East Atlantic was conducted between 1980 and 2018, collecting 2.68 million km of data. The data was collated and standardized and used to create distribution maps for 12 cetacean and 12 seabird species. These maps are at a resolution of 10 km and are at a monthly temporal scale.

Data was preprocessed and filtered to the study areas spatial and temporal resolution. For this initial analysis oil spills in the month of August and one species distribution in August, Harbour porpoise, was selected for the analysis. After data cleaning, zonal statistics were generated from each oil spill resulting in an average animal per km2 value for each oil spill.

SkyTruth, 2023, https://skytruth.org

Waggitt, J. J., Evans, P. G., Andrade, J., Banks, A. N., Boisseau, O., Bolton, M., ... & Hiddink, J. G. (2020). Distribution maps of cetacean and seabird populations in the North‐East Atlantic. Journal of Applied Ecology, 57(2), 253-269.

In [1]:
# Import libraries
import os

import folium
import geopandas as gpd
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import plotly.express as px
import rasterio
import rioxarray as rxr

# Set path to working directory
working_dir = "/Users/ryanboarman/Documents/WORK/2023/skytruth/data"
# Change working directory
os.chdir(working_dir)

Harbour Porpoise Distribution in August¶

Preprocess Harbour porpoise data¶

In [2]:
# Set path for Harbour Porpoise August
harbourporp_08_path = os.path.join(
    working_dir, 'ASCII', 'Fitted',
    'MERP_EUR_HarbourPorpoiseFitted_08_EUROPE_UTM30N.asc')
# Read in Harbour Porpoise Aug using xarray
data = rxr.open_rasterio(harbourporp_08_path)
# Set CRS to 32630
data = data.rio.set_crs("EPSG:32630")
# Replace -9999 with nan
data_no_nan = data.where(data != -9999)
# Set CRS to 32630
data_no_nan = data_no_nan.rio.set_crs("EPSG:32630")

Map Harbour porpoise August¶

In [3]:
# Plot Harbour Porpoise August
fig, ax = plt.subplots(figsize=(6, 6))

# Set the title
ax.set_title('Harbour Porpoise\nAugut Distribution')
# Show image
shw = ax.imshow(data_no_nan[0])
# Make bar
bar = plt.colorbar(shw)
# Show plot with labels
plt.xlabel('')
plt.ylabel('')
bar.set_label('Animal Density (animal/km2)')
# Remove the tick labels
ax.set_xticklabels([])
ax.set_yticklabels([])

plt.show()

The figure shows animal density (animal/km2) in the north sea. Yellow regions highlight higher density regions while dark blue shows low density.

Oil Slicks in August¶

Preprocess oil slick data¶

In [4]:
# Set oil slick paths for each country
germany_path = ('/Users/ryanboarman/Documents/WORK/2023/skytruth/'
                'data/oildata1/germany_slicks.geojson')
finland_path = ('/Users/ryanboarman/Documents/WORK/2023/skytruth/'
                'data/oildata1/finland_slicks.geojson')
norway_path = ('/Users/ryanboarman/Documents/WORK/2023/skytruth/'
               'data/oildata1/norway_slicks.geojson')
sweden_path = ('/Users/ryanboarman/Documents/WORK/2023/skytruth/'
               'data/oildata1/sweden_slicks.geojson')
uk_path = ('/Users/ryanboarman/Documents/WORK/2023/skytruth/'
           'data/oildata1/uk_slicks.geojson')
# Read in each oil slick to gdf
germany_gdf = gpd.read_file(germany_path)
finland_gdf = gpd.read_file(finland_path)
norway_gdf = gpd.read_file(norway_path)
sweden_gdf = gpd.read_file(sweden_path)
uk_gdf = gpd.read_file(uk_path)
# Convert the datetime fields to strings
germany_gdf['grd__starttime'] = germany_gdf['grd__starttime'].astype(str)
finland_gdf['grd__starttime'] = finland_gdf['grd__starttime'].astype(str)
norway_gdf['grd__starttime'] = norway_gdf['grd__starttime'].astype(str)
sweden_gdf['grd__starttime'] = sweden_gdf['grd__starttime'].astype(str)
uk_gdf['grd__starttime'] = uk_gdf['grd__starttime'].astype(str)
# Concatenate the GeoDataFrames
all_countries_gdf = pd.concat(
    [germany_gdf,
     finland_gdf,
     norway_gdf,
     sweden_gdf,
     uk_gdf], ignore_index=True)

# Replace "NULL" values with NaN
all_countries_gdf['slick__class_int'] = (all_countries_gdf['slick__class_int']
                                         .replace('NULL', np.nan))
# Replace NaN values with -999
all_countries_gdf['slick__class_int'] = (all_countries_gdf['slick__class_int']
                                         .fillna(-999))
# Convert column slick__class_int to int
all_countries_gdf['slick__class_int'] = (all_countries_gdf['slick__class_int']
                                         .astype(int))
# Select oil slick category 4,6,18,19
all_countries_gdf_allcat = (
    all_countries_gdf[all_countries_gdf['slick__class_int'].isin([
        4, 6, 18, 19])])
# Define the target CRS
crs_target = "EPSG:32630"
# Transform the GeoDataFrame to the target CRS
all_countries_gdf_allcat = all_countries_gdf_allcat.to_crs(crs_target)
# Convert the 'grd__starttime' column to datetime format
all_countries_gdf_allcat['grd__starttime'] = pd.to_datetime(
    all_countries_gdf_allcat['grd__starttime'])
# Create a boolean mask to select rows that are in the month of August
mask = all_countries_gdf_allcat['grd__starttime'].dt.month == 8
# Apply the boolean mask to select the rows
all_countries_gdf_aug = all_countries_gdf_allcat[mask]

# Crop oil spills to ROI
# Get the bounds of the raster dataset
bounds = data_no_nan.rio.bounds()

# Clip the oil to the bounds of the raster dataset
all_countries_gdf_aug_clipped = gpd.clip(all_countries_gdf_aug, bounds)

Map of Oil Slicks in August¶

In [5]:
# Create a new map
map = folium.Map(location=[60.4720, 9.4689],
                 zoom_start=4, width="75%", height="75%")
# Convert the 'grd__starttime' column to a string format that can be serialized to JSON
all_countries_gdf_aug_clipped['grd__starttime'] = (
    all_countries_gdf_aug_clipped['grd__starttime'].astype(str))
# Define the style function to set the color to red

def style_function(feature):
    """
    Returns a dictionary of style properties for a given GeoJSON feature.

    Returns:
        dict: A dictionary of style properties for the feature. 
            - 'fillColor': A string representing the fill color of the feature.
            - 'color': A string representing the color of the feature outline.
            - 'weight': An integer representing the width of the feature outline.
            - 'fillOpacity': A float representing the opacity of the feature fill.

    """
    return {
        'fillColor': 'red',
        'color': 'red',
        'weight': 3,
        'fillOpacity': 0.5,
    }


# Add the GeoDataFrame to the map with the style function applied
folium.GeoJson(all_countries_gdf_aug_clipped,
               style_function=style_function).add_to(map)

# Show the map
map
Out[5]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Figure highlights oil spill locations in red for August 2020, detected by SkyTruth's Cerulean project.

Map of Oil Slicks and Harbour Porpoise Distributions in August¶

In [6]:
fig, ax = plt.subplots(figsize=(10, 8))

# Plot the data on a map using matplotlib
shw = data_no_nan.plot(ax=ax, add_colorbar=False)

# Plot the polygon layer on top of the raster layer, using zorder to ensure it is on top
all_countries_gdf_aug_clipped.plot(
    ax=plt.gca(), facecolor='none', edgecolor='red', zorder=10)

# Add a color bar
cbar = fig.colorbar(shw)
cbar.set_label('Animal Density (animal/km2)')

# Set the title
ax.set_title('Augut: Oil Slicks and \nHarbour Porpoise Distribution')
plt.xlabel('')
plt.ylabel('')
# Remove the tick labels
ax.set_xticklabels([])
ax.set_yticklabels([])
# Create the custom legend
country_edgecolor = mpatches.Patch(facecolor='red',
                                   edgecolor='red', label='Oil Spills')
# Add legend
ax.legend(handles=[country_edgecolor], loc='lower right', fontsize=12)


plt.show()

Figure overlays August 2020 oil spills over August Harbour porpoise distribution map

Extract Harbour Poproise Distribution Statistics from Oil Slicks in August¶

In [7]:
# Number of Oil Spills
number_oilspills= all_countries_gdf_aug_clipped['posi_poly__id'].count()
print('The total number of Oil Spills are'  f' {number_oilspills}')
The total number of Oil Spills are 171

Extract Statistics¶

In [8]:
# Create blank lists for min, max, mean
min_vals = []
max_vals = []
mean_vals = []

# Loop through each geometry in the GeoDataFrame
for i, row in all_countries_gdf_aug_clipped.iterrows():

    # Initialize min, max, and mean values to NaN
    min_val = np.nan
    max_val = np.nan
    mean_val = np.nan
    try:
        # Clip the raster to the geometry
        clipped = data_no_nan.rio.clip([row.geometry], all_touched=True)

        # Mask out negative values
        clipped = clipped.where(clipped >= 0)

        # Compute the desired statistics for the clipped raster
        min_val = clipped.min().values
        max_val = clipped.max().values
        mean_val = clipped.mean().values

    except rxr.exceptions.NoDataInBounds:
        # If there is no data in the bounds of the geometry, set min, max, and mean values to NaN
        min_val = np.nan
        max_val = np.nan
        mean_val = np.nan

    # Append the min, max, and mean values to their respective lists
    min_vals.append(min_val)
    max_vals.append(max_val)
    mean_vals.append(mean_val)

# Add new columns to the GeoDataFrame with the min, max, and mean values
all_countries_gdf_aug_clipped['min_val'] = min_vals
all_countries_gdf_aug_clipped['max_val'] = max_vals
all_countries_gdf_aug_clipped['mean_val'] = mean_vals

Graph Statistics¶

Oil spills by Exclusive Economic Zone¶

In [9]:
# Calculate the area in square kilometers and store it in the 'area_km2' column
all_countries_gdf_aug_clipped['area_km2'] = (
    all_countries_gdf_aug_clipped['geometry'].area / 1e6)

# Group the data by 'eez__geoname' and aggregate the count and sum
table = all_countries_gdf_aug_clipped.groupby('eez__geoname').agg(
    {'eez__geoname': 'count', 'area_km2': 'sum'})

# Rename the columns of the table
table.columns = ['Number of Oil Spills', 'Total area (km2)']
table = table.rename_axis('Exclusive Economic Zone')

# Print the table
print(table)

# Plot table data
data = {
    'EEZ': ['German EEZ', 'Norwegian EEZ', 'UK EEZ'],
    'Oil Spills': [10, 33, 128],
    'Total area (km2)': [19.379211, 115.404001, 349.010083]
}

df = pd.DataFrame(data)

# Create a new column in the DataFrame with the desired text
df['Text'] = 'Number of Oil Spills: ' + df['Oil Spills'].astype(str)
# Set figure
fig = px.bar(df, x='EEZ', y='Total area (km2)', color='EEZ', text='Text',
             title='Oil Spills in Exclusive Economic Zones in August 2020',
             labels={'EEZ': 'Exclusive Economic Zone',
                     'Total area (km2)': 'Total Area (km2)'})
fig.update_traces(textposition='outside')
fig.update_layout(showlegend=False)
fig.show()
                                        Number of Oil Spills  Total area (km2)
Exclusive Economic Zone                                                       
German Exclusive Economic Zone                            10         19.379211
Norwegian Exclusive Economic Zone                         33        115.404001
United Kingdom Exclusive Economic Zone                   128        349.010083

Table and figure highlight both the number of oil spills and total area of the oil spills in August 2020 by the EEZ. The UK had much higher amounts and total areas of oil spills compared to Norway and German.

In [10]:
# Extract the mean_val data for each country

# Make country list
country_list = ['Norwegian Exclusive Economic Zone',
                'United Kingdom Exclusive Economic Zone',
                'German Exclusive Economic Zone']
# Make color list
colors = ['red', 'blue', 'green', 'orange', 'purple']
# Groupy by EEZ
data = ([all_countries_gdf_aug_clipped
         [all_countries_gdf_aug_clipped['eez__geoname']
          == eez__geoname]['mean_val'] for eez__geoname in country_list])

# Create a histogram plot with customizations
fig, ax = plt.subplots(figsize=(8, 5))
ax.hist(data, bins=15, stacked=True, color=colors[:len(
    country_list)], edgecolor='black', alpha=0.7, label=country_list)

ax.set_title('Harbour Porpoise Density per Oil Spill', fontsize=16)
ax.set_xlabel('Animal density (individuals per km2)', fontsize=14)
ax.set_ylabel('No. of Oil Spills', fontsize=14)

# Customize the appearance of the plot
ax.grid(True, linestyle='-', alpha=0.7)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.tick_params(axis='both', which='major', labelsize=12)

ax.legend(loc='best', fontsize=10)

plt.show()

Figure shows number of individual oil spills by EEZ and the density of Harbour porpoises effected by the spills.

Conclusion and Discussion¶

In this exploratory analysis, I have successfully demonstrated the potential of incorporating SkyTruth's Oil Spill SAR detection algorithm into a workflow for assessing the impact of oil spills originating from platforms and ships on the surrounding marine wildlife. Throughout August 2020, a total of 171 oil spills were detected from vessels and offshore infrastructure in the North Sea. While these incidents are not considered major oil spills, their cumulative effect on the environment is often underestimated, leading to significant harm to wildlife and ecosystems over time.

A comparative analysis of oil spills in the United Kingdom, Norway, and Germany revealed that the UK experienced the highest number of oil spills, affecting the largest areas. However, when considering the impact on a specific species, such as the Harbour porpoise, the UK was not necessarily the most destructive. Germany, with 10 oil spills covering a total area of 19.4 km2, had a smaller affected area compared to the UK. Nevertheless, these spills occurred in regions with higher densities of Harbour porpoise habitats, suggesting a potentially more significant impact on this species.

This key finding highlights the importance of considering not only the number and extent of oil spills but also their location in relation to specific species' habitats. In the case of the Harbour porpoise, even smaller spills can have considerable consequences on their health and well-being. The integration of SkyTruth's Oil Spill SAR detection algorithm into the workflow allows for a more comprehensive understanding of the effects of oil spills on marine wildlife, enabling more effective conservation and management strategies.

Future Research and Next Steps¶

The present study serves as a preliminary use case for a more comprehensive tool I plan to develop. Utilizing one month of data and focusing on a single species, I was able to validate the workflow and obtain intriguing findings. Building upon these initial results, I will expand the scope of the research to incorporate oil spill data from the entire year (January through December 2020) and examine the effects on 12 marine mammal species' distributions on a monthly basis.

This enhanced tool will provide valuable insights into the direct impact of small oil spills from offshore infrastructure and vessels in the North Sea on the habitats of 12 marine mammal species. By broadening the dataset and considering a wider range of species, the refined tool will contribute to a more accurate understanding of the complex interplay between oil spills and marine ecosystems, as well as inform more effective conservation and management strategies.

Future research could also explore the integration of additional data sources, such as oceanographic and meteorological parameters, to better account for environmental conditions that may influence the dispersal and impact of oil spills. Moreover, expanding the geographical scope of the study and applying the developed tool to other regions would enhance the generalizability and applicability of my findings, ultimately contributing to the global efforts in protecting marine wildlife from the detrimental effects of oil spills.